import styled from 'styled-components';
import React, { CSSProperties, useState, useCallback, useEffect, useRef, ReactNode, memo, useMemo } from 'react';
import EXAMPLE_DATA from './proj.json'
import { setAutoFreeze } from 'immer'
import {
    useEventListener,
} from 'ahooks'
import Dagre from '@dagrejs/dagre';
import {
    useStoreApi,
    ReactFlowProvider,
    useNodesState,
    useEdgesState,
    useReactFlow,
    Panel,
    addEdge,
    ConnectionLineType,
    ControlProps,
    useOnViewportChange,
    MiniMap,
    ReactFlow, Controls, Background,
    SelectionMode
} from 'reactflow';
import type {
    Viewport,
} from 'reactflow'
import 'reactflow/dist/style.css'

import { CanvasToolbar, WorkflowToolbar, WorkflowConnectionLine } from './components';
import { WorkflowContextProvider } from './context';
import {
    useStore,
    useWorkflowStore,
} from './store'
import {
    initialEdges,
    initialNodes,
} from './utils'
import {
    Edge,
    EnvironmentVariable,
    Node,
    ControlMode,
    SupportUploadFileTypes,
} from './types';
import {
    useDSL, useWorkflow, useShortcuts,
    usePanelInteractions, useNodesSyncDraft, useWorkflowUpdate,
    useEdgesInteractions, useSelectionInteractions,
    useWorkflowReadOnly, useNodesReadOnly,
    useWorkflowInit, useNodesInteractions
} from './hooks';
import {
    CUSTOM_NODE,
    DSL_EXPORT_CHECK,
    ITERATION_CHILDREN_Z_INDEX,
    WORKFLOW_DATA_UPDATE,
} from './constants'
import { FeaturesProvider } from '@/components/base/features';
import type { Features as FeaturesData } from '@/components/base/features/types'
import { WorkflowHistoryProvider } from './workflow-history-store';
import CustomNoteNode from './nodes/note-node'
import { CUSTOM_NOTE_NODE } from './nodes/note-node/constants'
import CustomIterationStartNode from './nodes/iteration-start'
import { CUSTOM_ITERATION_START_NODE } from './nodes/iteration-start/constants'
import { FILE_EXTS } from '@/components/base/prompt-editor/constants'
import { FileUploadConfigResponse } from '@/models/common';
import { useFeaturesStore } from '@/components/base/features/hooks'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { WorkflowEdge } from './edges';
import { WorkflowNode } from './nodes';
import Operator from './operator'
import { WorkflowFeatures } from './features'
import Confirm from '@/components/base/confirm'
import Loading from '@/components/base/loading'

import SyncingDataModal from './components/syncing-data-modal'
import DSLExportConfirmModal from './components/dsl-export-confirm-modal'
import LimitTips from './components/limit-tips'
import PanelContextmenu from './components/panel-contextmenu'
import NodeContextmenu from './components/node-contextmenu'
import UpdateDSLModal from './components/update-dsl-modal'
import CandidateNode from './nodes/candidate-node'
import Header from './components/header'
import HelpLine from './help-line'
import WorkflowPanel from './panel'

const nodeTypes = {
    [CUSTOM_NODE]: WorkflowNode,
    [CUSTOM_NOTE_NODE]: CustomNoteNode,
    [CUSTOM_ITERATION_START_NODE]: CustomIterationStartNode,
}
const edgeTypes = {
    "custom": WorkflowEdge,
}

export interface WorkflowCanvasProps {
    style?: CSSProperties;
    nodes: Node[];
    edges: Edge[];
    viewport?: Viewport;
}

const WorkflowCanvas: React.FC<WorkflowCanvasProps> = memo((props) => {
    const {
        nodes: originalNodes,
        edges: originalEdges,
        viewport,
    } = props;

    const workflowContainerRef = useRef<HTMLDivElement>(null)
    const workflowStore = useWorkflowStore()
    const reactflow = useReactFlow()
    const featuresStore = useFeaturesStore()

    const store = useStoreApi()

    const [nodes, setNodes] = useNodesState(originalNodes);
    const [edges, setEdges] = useEdgesState(originalEdges);

    const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
    const controlMode = useStore(s => s.controlMode)
    const nodeAnimation = useStore(s => s.nodeAnimation)
    const showConfirm = useStore(s => s.showConfirm)
    const showImportDSLModal = useStore(s => s.showImportDSLModal)

    const { workflowReadOnly } = useWorkflowReadOnly();
    const { nodesReadOnly } = useNodesReadOnly()

    const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])

    const { eventEmitter } = useEventEmitterContextContext()

    const {
        setShowConfirm,
        setControlPromptEditorRerenderKey,
        setShowImportDSLModal,
        setSyncWorkflowDraftHash,
    } = workflowStore.getState()
    const {
        handleSyncWorkflowDraft,
        syncWorkflowDraftWhenPageClose,
    } = useNodesSyncDraft()

    eventEmitter?.useSubscription((v: any) => {
        if (v.type === WORKFLOW_DATA_UPDATE) {
            setNodes(v.payload.nodes)
            setEdges(v.payload.edges)

            if (v.payload.viewport)
                reactflow.setViewport(v.payload.viewport)

            if (v.payload.features && featuresStore) {
                const { setFeatures } = featuresStore.getState()

                setFeatures(v.payload.features)
            }

            if (v.payload.hash)
                setSyncWorkflowDraftHash(v.payload.hash)

            setTimeout(() => setControlPromptEditorRerenderKey(Date.now()))
        }
        if (v.type === DSL_EXPORT_CHECK)
            setSecretEnvList(v.payload.data as EnvironmentVariable[])
    })

    useEffect(() => {
        setAutoFreeze(false)
        return () => {
            setAutoFreeze(true)
        }
    }, [])

    useEffect(() => {

        return () => {
            handleSyncWorkflowDraft(true, true)
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    const { handleRefreshWorkflowDraft } = useWorkflowUpdate()
    const handleSyncWorkflowDraftWhenPageClose = useCallback(() => {
        if (document.visibilityState === 'hidden')
            syncWorkflowDraftWhenPageClose()
        else if (document.visibilityState === 'visible')
            setTimeout(() => handleRefreshWorkflowDraft(), 500)
    }, [syncWorkflowDraftWhenPageClose, handleRefreshWorkflowDraft])

    useEffect(() => {
        document.addEventListener('visibilitychange', handleSyncWorkflowDraftWhenPageClose)

        return () => {
            document.removeEventListener('visibilitychange', handleSyncWorkflowDraftWhenPageClose)
        }
    }, [handleSyncWorkflowDraftWhenPageClose])


    useEventListener('keydown', (e) => {
        if ((e.key === 'd' || e.key === 'D') && (e.ctrlKey || e.metaKey))
            e.preventDefault()
        if ((e.key === 'z' || e.key === 'Z') && (e.ctrlKey || e.metaKey))
            e.preventDefault()
        if ((e.key === 'y' || e.key === 'Y') && (e.ctrlKey || e.metaKey))
            e.preventDefault()
        if ((e.key === 's' || e.key === 'S') && (e.ctrlKey || e.metaKey))
            e.preventDefault()
    })
    useEventListener('mousemove', (e) => {
        const containerClientRect = workflowContainerRef.current?.getBoundingClientRect()

        if (containerClientRect) {
            workflowStore.setState({
                mousePosition: {
                    pageX: e.clientX,
                    pageY: e.clientY,
                    elementX: e.clientX - containerClientRect.left,
                    elementY: e.clientY - containerClientRect.top,
                },
            })
        }
    })

    const {
        handleNodeDragStart,
        handleNodeDrag,
        handleNodeDragStop,
        handleNodeEnter,
        handleNodeLeave,
        handleNodeClick,
        handleNodeConnect,
        handleNodeConnectStart,
        handleNodeConnectEnd,
        handleNodeContextMenu,
        handleHistoryBack,
        handleHistoryForward,
    } = useNodesInteractions();
    const {
        handleEdgeEnter,
        handleEdgeLeave,
        handleEdgesChange,
    } = useEdgesInteractions();
    const {
        handleSelectionStart,
        handleSelectionChange,
        handleSelectionDrag,
    } = useSelectionInteractions();
    const {
        handlePaneContextMenu,
        handlePaneContextmenuCancel,
    } = usePanelInteractions();

    const {
        isValidConnection,
    } = useWorkflow()
    const {
        exportCheck,
        handleExportDSL,
    } = useDSL()

    useOnViewportChange({
        onEnd: () => {
            handleSyncWorkflowDraft()
        },
    })

    useShortcuts()

    if (process.env.NODE_ENV === 'development') {
        store.getState().onError = (code, message) => {
            if (code === '002')
                return
            console.warn(message)
        }
    }

    return (
        <Container id='workflow-container'
            ref={workflowContainerRef}
            className={`
                relative w-full min-w-[900px] h-full bg-[#F0F2F7]
                ${workflowReadOnly && 'workflow-panel-animation'}
                ${nodeAnimation && 'workflow-node-animation'}
            `}
            style={props.style}
        >
            <SyncingDataModal />
            <CandidateNode />
            <WorkflowPanel />
            {
                showFeaturesPanel && <WorkflowFeatures />
            }
            <PanelContextmenu />
            <NodeContextmenu />
            <HelpLine />
            {
                !!showConfirm && (
                    <Confirm
                        isShow
                        onCancel={() => setShowConfirm(undefined)}
                        onConfirm={showConfirm.onConfirm}
                        title={showConfirm.title}
                        content={showConfirm.desc}
                    />
                )
            }
            {
                showImportDSLModal && (
                    <UpdateDSLModal
                        onCancel={() => setShowImportDSLModal(false)}
                        onBackup={exportCheck}
                        onImport={handlePaneContextmenuCancel}
                    />
                )
            }
            {
                secretEnvList.length > 0 && (
                    <DSLExportConfirmModal
                        envList={secretEnvList}
                        onConfirm={handleExportDSL}
                        onClose={() => setSecretEnvList([])}
                    />
                )
            }
            <LimitTips />
            <ReactFlow
                nodeTypes={nodeTypes}
                edgeTypes={edgeTypes}
                nodes={nodes}
                edges={edges}

                // onNodesChange={onNodesChange}
                onNodeDragStart={handleNodeDragStart}
                onNodeDrag={handleNodeDrag}
                onNodeDragStop={handleNodeDragStop}

                onNodeMouseEnter={handleNodeEnter}
                onNodeMouseLeave={handleNodeLeave}
                onNodeClick={handleNodeClick}
                onNodeContextMenu={handleNodeContextMenu}

                onConnect={handleNodeConnect}
                onConnectStart={handleNodeConnectStart}
                onConnectEnd={handleNodeConnectEnd}

                onEdgeMouseEnter={handleEdgeEnter}
                onEdgeMouseLeave={handleEdgeLeave}
                onEdgesChange={handleEdgesChange}
                // onEdgesChange={onEdgesChange}

                onSelectionStart={handleSelectionStart}
                onSelectionChange={handleSelectionChange}
                onSelectionDrag={handleSelectionDrag}

                onPaneContextMenu={handlePaneContextMenu}
                connectionLineType={ConnectionLineType.SmoothStep}
                defaultViewport={viewport}
                isValidConnection={isValidConnection}
                connectionLineComponent={WorkflowConnectionLine}
                connectionLineContainerStyle={{ zIndex: ITERATION_CHILDREN_Z_INDEX }}
                multiSelectionKeyCode={null}
                deleteKeyCode={null}
                selectionKeyCode={null}
                selectionMode={SelectionMode.Partial}
                nodesDraggable={!nodesReadOnly}
                nodesConnectable={!nodesReadOnly}
                nodesFocusable={!nodesReadOnly}
                edgesFocusable={!nodesReadOnly}
                zoomOnPinch={!workflowReadOnly}
                zoomOnScroll={!workflowReadOnly}
                zoomOnDoubleClick={!workflowReadOnly}
                panOnDrag={controlMode === ControlMode.Hand && !workflowReadOnly}
                selectionOnDrag={controlMode === ControlMode.Pointer && !workflowReadOnly}
                fitView
                minZoom={0.25}
            >
                <Background
                    gap={[14, 14]}
                    size={2}
                    color='#E4E5E7'
                />
                <MiniMap
                    className='!absolute !left-8 !bottom-10 z-[9] !m-0 !w-[106px] !h-[106px] !border-[0.5px] !border-black/8 !shadow-lg'
                    style={{
                        width: 106,
                        height: 106,
                    }} />
                <Controls style={{ marginBottom: '40px', marginLeft: '5px' }} />
                <Panel position="bottom-left" style={{ marginLeft: '30px', marginBottom: '2px' }}>
                    <Operator handleRedo={handleHistoryForward} handleUndo={handleHistoryBack} />
                </Panel>
                <Panel position="top-right" style={{ marginTop: '0px', marginRight: '0px', width: '100%' }}>
                    <Header />
                </Panel>
            </ReactFlow>
        </Container>
    );
});

const Container = styled.div`
    width: 100%;
    height: 100%;
`;



const WorkflowWrap: React.FC<WorkflowProps> = memo((props) => {

    const {
        data,
        isLoading,
    } = useWorkflowInit(props.source)
    // const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
    let fileUploadConfigResponse: FileUploadConfigResponse = {
        batch_count_limit: 100,
        image_file_size_limit: '10MB',
        file_size_limit: 15,
        audio_file_size_limit: 50,
        video_file_size_limit: 100,
        workflow_file_upload_limit: 10,
    };
    // let data = EXAMPLE_DATA.workflow as any;

    const nodesData = useMemo(() => {
        console.log(data, '======');
        if (data)
            return initialNodes(data?.graph?.nodes || [], data?.graph?.edges || [])

        return []
    }, [data])

    const edgesData = useMemo(() => {
        if (data)
            return initialEdges(data.graph?.edges || [], data.graph?.nodes || [])

        return []
    }, [data])

    if (!data || isLoading) {
        return (
            <div className='flex justify-center items-center relative w-full h-full bg-[#F0F2F7]'>
                <Loading />
            </div>
        )
    }

    const features = data.features || {}
    const initialFeatures: FeaturesData = {
        file: {
            image: {
                enabled: !!features.file_upload?.image?.enabled,
                number_limits: features.file_upload?.image?.number_limits || 3,
                transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
            },
            enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
            allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
            allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
            allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
            number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
            fileUploadConfig: fileUploadConfigResponse,
        },
        opening: {
            enabled: !!features.opening_statement,
            opening_statement: features.opening_statement,
            suggested_questions: features.suggested_questions,
        },
        suggested: features.suggested_questions_after_answer || { enabled: false },
        speech2text: features.speech_to_text || { enabled: false },
        text2speech: features.text_to_speech || { enabled: false },
        citation: features.retriever_resource || { enabled: false },
        moderation: features.sensitive_word_avoidance || { enabled: false },
    }

    return <ReactFlowProvider>
        <WorkflowHistoryProvider nodes={nodesData} edges={edgesData} >
            <FeaturesProvider features={initialFeatures}>
                <WorkflowCanvas nodes={nodesData}
                    edges={edgesData}
                    viewport={data?.graph?.viewport}></WorkflowCanvas>
            </FeaturesProvider>
        </WorkflowHistoryProvider>
    </ReactFlowProvider>
});

export interface WorkflowProps {
    source: string;
}

const _Workflow: React.FC<WorkflowProps> = memo((props) => {
    return (
        <WorkflowContextProvider sourceFile={props.source}>
            <WorkflowWrap {...props} />
        </WorkflowContextProvider>
    )
})

export const Workflow = _Workflow;

export default Workflow

